#include "schrodinger.hpp"
#include "fighting.hpp"
#include "lines.hpp"
#include "types.h"
#include <cstdint>
#include <cstdio>
#include <ctime>

extern price_t g_hop;

namespace Trade {

const char*
sdg_name(const int8_t whoami)
{
  return (whoami == 1 ? "Tom" : (whoami == 2 ? "Jerry" : "Mickey"));
}

const uint8_t
sdg_color(const int8_t whoami)
{
  return (whoami == 1 ? 49 : (whoami == 2 ? 11 : 177));
}

// -----------------------------------------------------------------------
//
// Schrodinger
//
// -----------------------------------------------------------------------
void
Schrodinger::print(const char* title,
                   const int64_t idx,
                   const price_t hp_now,
                   const time_t t_now) noexcept
{
  if (dir) {
    const char* name = sdg_name(whoami);
    const uint8_t color = sdg_color(whoami);
    printf("\033[38;5;%um %s is running to %d: (%ld %ld mins) | since %ld(%ld) "
           "| case no %u\033[0m\n",
           color,
           title,
           dir,
           ((target.timer.t - t_now) / 60),
           ((target.timer_2.t - t_now) / 60),
           ((t_now - my_tm) / 60),
           my_idx,
           case_no);
    target.lines.print(idx, hp_now);
    char buf[64];
    snprintf(buf, 64, "\033[38;5;%um %s: \033[0m", color, name);
    print_reached(buf, dir, target.reached, idx);
    // em.print(name);
  }
}

bool
Schrodinger::set_timer(const time_t tm, const time_t tm2) noexcept
{
  target.set_timer(tm, tm2);
  return true;
}

bool
Schrodinger::update_lines(const Lines& l, const price_t hp_now) noexcept
{
  auto changed = target.update_lines(1, l, hp_now);
  auto c2 = target.update_lines(-1, l, hp_now);
  return (c2 or changed);
}

Timer*
Schrodinger::get_timer() noexcept
{
  return &target.timer;
}

Lines&
Schrodinger::get_limits_lines() noexcept
{
  return target.lines;
}

Spacetime*
Schrodinger::get_target() noexcept
{
  return &target;
}

uint8_t
Schrodinger::check_reached(const time_t t_now,
                           const price_t hp_now,
                           Lines& l_now,
                           const int64_t idx) noexcept
{
  if (is_running()) {
    uint8_t rchd = 0;
    rchd = target.check_reached(t_now, hp_now, l_now, idx);
    if (rchd & LIMIT_REACHED::REACHED_TIMER_OUT)
      ++amout_to;

    return rchd;
  }

  return (0);
}

void
Schrodinger::update_timer(const time_t tm, const time_t tm2) noexcept
{
  target.update_timer(tm, tm2);
}

uint8_t
Schrodinger::reacheded() const noexcept
{
  return target.reached;
}

void
Schrodinger::clear_reacheded() noexcept
{
  target.reached = 0;
}

bool
Schrodinger::reached_line2() noexcept
{
  bool rchd = 0;
  if (dir > 0) {
    rchd =
      (target.lines.nup > 1 && (target.reached & LIMIT_REACHED::REACHED_UP_2));
  } else {
    rchd =
      (target.lines.ndn > 1 && (target.reached & LIMIT_REACHED::REACHED_DN_2));
  }
  return rchd;
}

bool
Schrodinger::reached_line1() noexcept
{
  bool rchd = 0;
  if (dir > 0) {
    rchd =
      (target.lines.nup > 0 && target.reached & (LIMIT_REACHED::REACHED_UP_1 |
                                                 LIMIT_REACHED::REACHED_UP_2));
  } else {
    rchd =
      (target.lines.ndn > 0 && target.reached & (LIMIT_REACHED::REACHED_DN_1 |
                                                 LIMIT_REACHED::REACHED_DN_2));
  }
  return rchd;
}

bool
Schrodinger::reached_oppo_line1() noexcept
{
  bool rchd = 0;
  if (dir < 0) {
    rchd =
      (target.lines.nup > 0 && target.reached & (LIMIT_REACHED::REACHED_DN_1 |
                                                 LIMIT_REACHED::REACHED_DN_2));
  } else {
    rchd =
      (target.lines.ndn > 0 && target.reached & (LIMIT_REACHED::REACHED_UP_1 |
                                                 LIMIT_REACHED::REACHED_UP_2));
  }
  return rchd;
}

bool
Schrodinger::reached_oppo_line2() noexcept
{
  bool rchd = 0;
  if (dir < 0) {
    rchd =
      (target.lines.nup > 1 && (target.reached & LIMIT_REACHED::REACHED_UP_2));
  } else {
    rchd =
      (target.lines.ndn > 1 && (target.reached & LIMIT_REACHED::REACHED_DN_2));
  }
  return rchd;
}

bool
Schrodinger::timer_outed() noexcept
{
  return (amout_to > 0);
}

bool
Schrodinger::current_timer_outed() noexcept
{
  return (target.reached & LIMIT_REACHED::REACHED_TIMER_OUT);
}

void
Schrodinger::stop(const int64_t idx,
                  const int64_t tm [[maybe_unused]],
                  const uint32_t cno [[maybe_unused]]) noexcept
{
  if (whoami) {
    const uint8_t color = sdg_color(whoami);
    const char* name = sdg_name(whoami);
    printf("\033[38;5;%um %s has stoped. begin %ld, stop %ld, CNO %u\033[0m\n",
           color,
           name,
           my_idx,
           idx,
           cno);
    reset();
  }
}

void
Schrodinger::run(const dir_t d,
                 const time_t tm1,
                 const time_t tm2,
                 const Lines& lines,
                 const price_t hp_now,
                 const time_t t_now,
                 const int64_t idx,
                 const case_t cno,
                 const int whoami) noexcept
{
  reset();
  this->whoami = whoami;
  dir = d;
  auto t_min = std::min(tm1, tm2);
  auto t_max = std::max(tm1, tm2);
  set_timer(t_min, t_max);
  update_lines(lines, hp_now);
  my_tm = t_now;
  my_hp = hp_now;
  my_idx = idx;
  target.reached = 0;

  memset((void*)(&incomming), 0, sizeof(Incomming));
  incomming.update_revenue(hp_now, d, hp_now, 1, t_now, idx);
  os_entries = hp_now;
  status = PET_STATUS::NORMAL;
  auto name = sdg_name(whoami);
  print(name, idx, hp_now, t_now);
}

bool
Schrodinger::d5_was_reversed(const Dir5& d5) noexcept
{
  return false;
}

void
Schrodinger::update_g2(const WaitingForWrong& wfw, const Wave& wave) noexcept
{
}

bool
Schrodinger::should_renew_lines(const Lines& l,
                                const time_t t_now,
                                const price_t hp_now) noexcept
{
  auto good_space_for_new_lines = [&]() {
    auto new_space = (dir > 0) ? (l.hp_up_2 - hp_now) : (hp_now - l.hp_dn_2);
    auto l2_delta = (dir > 0) ? (l.hp_up_2 - target.lines.hp_up_2)
                              : (target.lines.hp_dn_2 - l.hp_dn_2);
    bool yes =
      (new_space >= 9.9999) && (l2_delta >= 9.9999) && (l2_delta <= 40.0000);
    return yes;
  };

  if (!good_space_for_new_lines())
    return false;

  bool renew = false;
  if (timer_outed()) {
    if (dir > 0) {
      if (reached_line2()) {
        if (l.nup > 1 && (l.hp_up_2 - target.lines.hp_up_2 > 24.9999))
          renew = true;
      }
    } else {
      if (reached_line2()) {
        if (l.ndn > 1 && (target.lines.hp_dn_2 - l.hp_dn_2 > 24.9999))
          renew = true;
      }
    }
  }
  return renew;
}

void
Schrodinger::erase_opposite_reached(const uint8_t rchd) noexcept
{
  if (dir) {
    target.erase_opposite_reached(dir, rchd);
  }
}

void
Schrodinger::renew_target(const Lines& lines,
                          const time_t tm,
                          const time_t t_now,
                          const price_t hp_now,
                          const int64_t idx) noexcept
{
  target.reached = 0;
  target.lines = lines;
  target.set_timer(tm, 0);

  printf(
    "\033[38;5;200m ========= Cat renew Target %d: %ld mins ======== \033[0m",
    dir,
    (tm - t_now) / 60);
  target.lines.print(idx, hp_now);
}

#if (ONLINE_DATA_COMM)
int
Schrodinger::get_json(char* buf, const int buf_len, const char* name)
{
  memset(buf, 0, buf_len);
  const char* format = "{\"type\":50,\"name\":\"%s\",\"dir\":%d,\"line1\":%d,"
                       "\"line2\":%d,\"to\":%"
                       "d,\"tm\":%ld,\"up1\":%.02lf,\"up2\":%.02lf,\"dn1\":%."
                       "02lf,\"dn2\":%.02lf}";
  bool reached_l1 = reached_line1();
  bool reached_l2 = reached_line2();
  bool to = timer_outed();

  price_t u1 = 0, u2 = 0, d1 = 0, d2 = 0;
  if (target.lines.nup > 0)
    u1 = target.lines.hp_up_1;
  if (target.lines.nup > 1)
    u2 = target.lines.hp_up_2;
  else
    u2 = u1;

  if (target.lines.ndn > 0)
    d1 = target.lines.hp_dn_1;
  if (target.lines.ndn > 1)
    d2 = target.lines.hp_dn_2;
  else
    d2 = d1;

  snprintf(buf,
           buf_len,
           format,
           name,
           dir,
           reached_l1,
           reached_l2,
           to,
           target.timer.t,
           u1,
           u2,
           d1,
           d2);
  int len = strlen(buf);
  return len;
}
#endif // ONLINE

#define MINUTES(tm, t_now) ((tm) ? (int((tm) - (t_now)) / 60) : 0)

// -----------------------------------------------------------------------
//
// Schrodinger
//
// -----------------------------------------------------------------------
void
Schrodinger::print_revenue(const price_t hp_now,
                           const time_t t_now) const noexcept
{
  if (dir) {
    const uint8_t color = sdg_color(whoami);
    // const char* rvn_str = (incomming.rvn_total > 0) ? "Won" : "Lost";
    // const char* rvn_str = (incomming.rvn_total > 0) ? "W" : "L";
    const char* name = sdg_name(whoami);
    printf("\033[38;5;%um %s %d, %.02lfh (w%.02lf %ld, l%.02lf %ld) T "
           "%d Du %d TO %d R%.02lf\033[0m\n",
           color,
           name,
           dir,
           incomming.rvn_single,
           incomming.rvn_max,
           incomming.idx_rvn_max,
           incomming.rvn_min,
           incomming.idx_rvn_min,
           MINUTES(target.timer.t, t_now),
           int(t_now - my_tm) / 60,
           amout_to,
           incomming.get_loss_rate());

    print_reached(sdg_name(whoami), dir, target.reached, 0);
  }
}

uint8_t
Schrodinger::reached(const time_t t_now,
                     const price_t hp_now,
                     Lines& l_now,
                     const int64_t idx) noexcept
{
  if (is_running()) {
    uint8_t rchd = 0;
    rchd = target.check_reached(t_now, hp_now, l_now, idx);
    if (rchd & LIMIT_REACHED::REACHED_TIMER_OUT)
      ++amout_to;

    return rchd;
  }

  return (0);
}

void
Schrodinger::print(const char* title,
                   const int64_t idx,
                   const price_t hp_now,
                   const time_t t_now) const noexcept
{
  // Schrodinger::print(title, idx, hp_now, t_now);
  // if (dir) {
  //   // os_entrance.print(hp_now);
  //   // os_exit.print(hp_now);
  //   printf("TO times %u\n", amout_to);
  //   print_revenue(hp_now);
  // }
  if (dir) {
    print_revenue(hp_now, t_now);
    target.lines.print(idx, hp_now);
  }
}

bool
Schrodinger::second_timer_outed(const time_t t_now) noexcept
{
  return target.second_timer_outed(t_now);
}

int
Schrodinger::get_durantion(const time_t t_now) noexcept
{
  return int(t_now - my_tm) / 60;
}

void
Schrodinger::reset() noexcept
{
  Incomming in;
  in.copy(&incomming);
  auto mock_backup = mock;
  memset((void*)(this), 0, sizeof(Schrodinger));
  mock = mock_backup;
  status = PET_STATUS::NONE;
  incomming.copy(&in);
}

void
Schrodinger::copy_from(const Schrodinger* sdg) noexcept
{
  memcpy((void*)(this), sdg, sizeof(Schrodinger));
}

void
Schrodinger::renew_lines(const dir_t d,
                         const Lines& ls,
                         const price_t hp_now) noexcept
{
  if (d < 0) {
    if (target.lines.reached & LIMIT_REACHED::REACHED_DN_1)
      target.lines.reached ^= LIMIT_REACHED::REACHED_DN_1;
    if (target.lines.reached & LIMIT_REACHED::REACHED_DN_2)
      target.lines.reached ^= LIMIT_REACHED::REACHED_DN_2;

    prev_lines = target.lines;

    target.lines.ndn = ls.ndn;
    target.lines.hp_dn_1 = ls.hp_dn_1;
    target.lines.hp_dn_2 = ls.hp_dn_2;
    target.lines.limit_reached(hp_now);

  } else if (d > 0) {
    if (target.lines.reached & LIMIT_REACHED::REACHED_UP_1)
      target.lines.reached ^= LIMIT_REACHED::REACHED_UP_1;
    if (target.lines.reached & LIMIT_REACHED::REACHED_UP_2)
      target.lines.reached ^= LIMIT_REACHED::REACHED_UP_2;

    prev_lines = target.lines;

    target.lines.nup = ls.nup;
    target.lines.hp_up_1 = ls.hp_up_1;
    target.lines.hp_up_2 = ls.hp_up_2;
    target.lines.limit_reached(hp_now);
  }
}

void
Schrodinger::reached_additional_line2(const dir_t d, const price_t hpx) noexcept
{
  if (dir == d) {
    amount_rl2++;
    reached_line = hpx;
  }
}

} // namespace Trade
